package bulkyshare.project.de.bulkyshare.activities;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.location.Address;
import android.location.Geocoder;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CalendarView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.Toast;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import bulkyshare.project.de.bulkyshare.R;
import bulkyshare.project.de.bulkyshare.container.Bulk;
import bulkyshare.project.de.bulkyshare.container.Contact;
import bulkyshare.project.de.bulkyshare.container.Profil;
import bulkyshare.project.de.bulkyshare.container.Termin;
import bulkyshare.project.de.bulkyshare.dialogs.AddBulkyItemDialog;
import bulkyshare.project.de.bulkyshare.dialogs.AddContactItemDialog;
import bulkyshare.project.de.bulkyshare.tasks.TaskHandler;
import bulkyshare.project.de.bulkyshare.views.OnListViewTouch;
import bulkyshare.project.de.bulkyshare.views.ScrollViewCalendar;

/**
 * @author Alexander Radtke
 * @version 1.0
 *
 * This class adds a pickup date to the database and edits one if it is already existing. It also
 * verifies all the user input (Date, Address, Bulky items, Contacts, ...).
 */
public class CreatePickupDate extends Activity implements AdapterView.OnClickListener, AdapterView.OnItemClickListener,
                    CalendarView.OnDateChangeListener {
    private EditText street_name, postal_code, price, extra_info;
    private Spinner cost_type;
    private ListView bulky_items, contact_items;
    private ArrayList<Bulk> items;
    private ArrayList<Contact> contacts;
    private ArrayAdapter<String> bulk_adapter, contact_adapter;
    private String date = "", user_id;
    private double longitude, latitude;
    private ScrollViewCalendar calendarView;
    private Termin edit_termin;

    private TaskHandler taskHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.create_pickup_date);
        setTitle(R.string.create_pickup_title);

        Button add = (Button) findViewById(R.id.add_bulky_item);
        Button publish = (Button) findViewById(R.id.publish_date);
        Button add_contact = (Button) findViewById(R.id.add_contact);

        Bundle info = getIntent().getExtras();
        if (info != null) {
            user_id = info.getString("user_id");
            init();
            // If this class is called for editing a pickup date perform some additional methods
            if (info.getBoolean("edit")) {
                publish.setVisibility(View.GONE);
                edit_termin = (Termin)info.get("edit_termin");
                setTitle(R.string.edit);
                initEdit();
            }
        }

        publish.setOnClickListener(this);
        add.setOnClickListener(this);
        add_contact.setOnClickListener(this);
        bulky_items.setOnItemClickListener(this);
        contact_items.setOnItemClickListener(this);
        // Register the context menu for both ListViews
        registerForContextMenu(bulky_items);
        registerForContextMenu(contact_items);
    }

    /**
     * This method initializes all elements from the layout and all other items that are
     * needed to create a new pickup date.
     */
    private void init() {
        items = new ArrayList<Bulk>();
        contacts = new ArrayList<Contact>();
        ArrayList<String> names = new ArrayList<String>();
        ArrayList<String> contact_values = new ArrayList<String>();

        calendarView = (ScrollViewCalendar) findViewById(R.id.pickup_date_calendar);
        calendarView.setOnDateChangeListener(this);

        street_name = (EditText) findViewById(R.id.streename_value);
        postal_code = (EditText) findViewById(R.id.postal_code_value);
        price = (EditText) findViewById(R.id.price_value);
        extra_info = (EditText) findViewById(R.id.extra_information_value);

        cost_type = (Spinner) findViewById(R.id.cost_type_spinner);

        contact_items = (ListView) findViewById(R.id.contact_list);
        contact_adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contact_values);
        contact_items.setAdapter(contact_adapter);
        contact_items.setOnTouchListener(new OnListViewTouch());
        setStandardMailAddress();

        bulky_items = (ListView) findViewById(R.id.bulky_items_list);
        bulk_adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, names);
        bulky_items.setAdapter(bulk_adapter);
        bulky_items.setOnTouchListener(new OnListViewTouch());
    }

    /**
     * This method is called when the user presses the submit button. It checks
     * all entered values for their existence and their correctness. If there is
     * an incorrect values an error message will be displayed.
     *
     * @return Returns true if everything is correct.
     */
    private boolean checkValues() {
        if (street_name.getText().toString().isEmpty() && postal_code.getText().toString().isEmpty()) {
            street_name.setError(getString(R.string.street_name_empty));
            postal_code.setError(getString(R.string.postal_code_empty));
            return false;
        } else if(street_name.getText().toString().isEmpty()) {
            street_name.setError(getString(R.string.street_name_empty));
            return false;
        } else if (postal_code.getText().toString().isEmpty()) {
            postal_code.setError(getString(R.string.postal_code_empty));
            return false;
        } else if (date.equals("")) {
            // Change error message type
            Toast.makeText(getApplicationContext(), getString(R.string.pickup_date_empty), Toast.LENGTH_SHORT).show();
            return false;
        }
        if (postal_code.getText().toString().length() != 5) {
            postal_code.setError(getString(R.string.invalid_postal_code));
            return false;
        }
        // If there is no price given by the user set it to zero
        if (price.getText().toString().isEmpty()) {
            price.setText("0");
        }
        // There must be at least one contact information to continue
        if (contacts.size() == 0) {
            Toast.makeText(getApplicationContext(), R.string.one_contact, Toast.LENGTH_SHORT).show();
            return false;
        }
        // Check street name and postal code with the Geocoder for existence
        if (!checkAddress(street_name.getText().toString(), postal_code.getText().toString())) return false;
        return true;
    }

    /**
     * This method adds a standard mail address to the contacts ListView.
     * It is always the mail address the user is using to login.
     */
    private void setStandardMailAddress() {
        try {
            taskHandler = new TaskHandler(this);
            taskHandler.setPara(new int[]{new Integer(user_id)});
            Profil user = (Profil) taskHandler.execute("getProfilId").get();
            Log.d("user", user.getMail());

            Contact contact = new Contact();
            contact.setContact(user.getMail());
            contact.setType(getResources().getStringArray(R.array.contact_types)[3]);
            contacts.add(contact);
            contact_adapter.add(user.getMail());
            contact_adapter.notifyDataSetChanged();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * This method checks if the user entered a valid Address.
     * @param street The street name, that was entered by the user
     * @param e_postal_code The postal code, that was entered by the user
     * @return Returns true if the entered Address exists and is within the postal code area
     */
    private boolean checkAddress(String street, String e_postal_code) {
        Geocoder geo = new Geocoder(this);
        if (geo.isPresent()) {
            try {
                String address = street + ", " + e_postal_code;
                List<Address> addresses = geo.getFromLocationName(address, R.integer.max_address_results);
                Address found = addresses.get(0);
                if (found != null && e_postal_code.equals(found.getPostalCode())) {
                    Log.d("Address", found.getAddressLine(0));
                    Log.d("Address", found.getAddressLine(1));
                    longitude = found.getLongitude();
                    latitude = found.getLatitude();
                    return true;
                } else {
                    street_name.setError(getString(R.string.address_not_found));
                    postal_code.setError(getString(R.string.address_not_found));
                    Log.e("checkAddress() Error", "No Address found");
                    return false;
                }
            } catch (IOException e) {
                Log.e("checkAddress() Error", e.getMessage());
                return false;
            }
        }
        return false;
    }

    /**
     * This method checks if a Calendar object is before another Calendar object
     * @param selected The Calendar object, that contains the selected date
     * @param today Calendar object for today
     * @return Returns true if the selected date is valid
     */
    private boolean checkDayBefore(Calendar selected, Calendar today) {
        if (selected.get(Calendar.YEAR) < today.get(Calendar.YEAR)) return false;
        if (selected.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) return false;
        if (selected.get(Calendar.DAY_OF_YEAR) < today.get(Calendar.DAY_OF_YEAR)) return false;
        return true;
    }

    /**
     * Handles clicks on all buttons in this View
     * @param view
     */
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.publish_date:
                // checkValues and add pickup date to database
                if(checkValues()) {
                    // Check was successful. Insert date into database
                    Termin insert = collectData(0);
                    // open database connection and add pickup date
                    try {
                        taskHandler = new TaskHandler(this);
                        taskHandler.setTermin(terminToList(insert));
                        int termin_id = (Integer) taskHandler.execute("createTermin").get();
                        taskHandler = new TaskHandler(this);
                        taskHandler.setPara(new int[]{new Integer(user_id), termin_id});
                        taskHandler.execute("createProfilTerminEntry");
                        taskHandler = new TaskHandler(this);
                        taskHandler.setPara(new int[]{termin_id});
                        taskHandler.setObjList(insert.getBulk());
                        taskHandler.execute("createBulk");
                        for (int i = 0; i < contacts.size(); i++) {
                            taskHandler = new TaskHandler(this);
                            taskHandler.setContact(contacts.get(i));
                            taskHandler.setPar(termin_id);
                            taskHandler.execute("createContact");
                        }
                        Toast.makeText(getApplicationContext(), R.string.create_date_success, Toast.LENGTH_SHORT).show();
                        finish();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                break;
            case R.id.edit_date:
                if (checkValues()) {
                    updateTermin();
                    Toast.makeText(getApplicationContext(), R.string.edit_date_success, Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            case R.id.add_bulky_item:
                // Open add bulky item Dialog
                final AddBulkyItemDialog add_dialog = new AddBulkyItemDialog(CreatePickupDate.this);
                add_dialog.show();
                // When the user presses the add button or the back key dismiss is called to close the dialog
                // only when the add button was pressed a bulk object will be returned. This object is added
                // to the list in onDismiss
                add_dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialogInterface) {
                        Bulk b = add_dialog.getItem();
                        if (b != null) {
                            items.add(b);
                            bulk_adapter.add(b.getName());
                            bulk_adapter.notifyDataSetChanged();
                        }
                    }
                });
                break;
            case R.id.add_contact:
                // Open add contact item Dialog
                final AddContactItemDialog add_contact = new AddContactItemDialog(CreatePickupDate.this);
                add_contact.show();
                add_contact.setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialogInterface) {
                        Contact contact = add_contact.getContact();
                        if (contact != null) {
                            contacts.add(contact);
                            contact_adapter.add(contact.getContact());
                            contact_adapter.notifyDataSetChanged();
                        }
                    }
                });
                break;
            default:
                // Do nothing on default, because on default there is not button pressed
                break;
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, view, menuInfo);
        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
        String[] values = getResources().getStringArray(R.array.context_menu_items);

        switch(view.getId()) {
            case R.id.bulky_items_list:
                menu.setHeaderTitle(items.get(info.position).getName());
                menu.add(Menu.NONE, 0, 0, values[0]);
                menu.add(Menu.NONE, 1, 1, values[1]);
                break;
            case R.id.contact_list:
                menu.setHeaderTitle(contacts.get(info.position).getContact());
                menu.add(Menu.NONE, 2, 0, values[0]);
                menu.add(Menu.NONE, 3, 1, values[1]);
                break;
            default:
                break;
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        switch (item.getItemId()) {
            case 0:
                // Case 0 is edit an item
                final AddBulkyItemDialog edit_bulk = new AddBulkyItemDialog(CreatePickupDate.this);
                edit_bulk.show();
                edit_bulk.setValuesForEdit(items.get(info.position));
                edit_bulk.setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialogInterface) {
                        Bulk b = edit_bulk.getItem();
                        if (b != null) {
                            if (!b.getName().equals(items.get(info.position).getName())) {
                                bulk_adapter.remove(items.get(info.position).getName());
                                items.get(info.position).setName(b.getName());
                                items.get(info.position).setDescription(b.getDescription());
                                items.get(info.position).setCondition(b.getCondition());
                                bulk_adapter.add(items.get(info.position).getName());
                                bulk_adapter.notifyDataSetChanged();
                            } else {
                                items.get(info.position).setDescription(b.getDescription());
                                items.get(info.position).setCondition(b.getCondition());
                            }
                        }
                    }
                });
                return true;
            case 1:
                // Case 1 is delete an item
                deleteBulkyItem(items.get(info.position).getName(), info.position);
                return true;
            case 2:
                // Edit a contact item
                final AddContactItemDialog edit_contact = new AddContactItemDialog(CreatePickupDate.this);
                edit_contact.show();
                edit_contact.setValuesForEdit(contacts.get(info.position));
                edit_contact.setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialogInterface) {
                        Contact c = edit_contact.getContact();
                        if (c != null) {
                            if (!c.getContact().equals(contacts.get(info.position).getContact())) {
                                contact_adapter.remove(contacts.get(info.position).getContact());
                                contacts.get(info.position).setContact(c.getContact());
                                contacts.get(info.position).setType(c.getType());
                                contact_adapter.add(contacts.get(info.position).getContact());
                                contact_adapter.notifyDataSetChanged();
                            } else {
                                contacts.get(info.position).setContact(c.getContact());
                                contacts.get(info.position).setType(c.getType());
                            }
                        }
                    }
                });
                return true;
            case 3:
                // Delete a contact item
                deleteContact(contacts.get(info.position).getContact(), info.position);
                return true;
            default:
                return super.onContextItemSelected(item);
        }
    }

    @Override
    public void onItemClick(AdapterView adapterView, View view, int pos, long id) {
        switch (adapterView.getId()) {
            case R.id.bulky_items_list:
                AddBulkyItemDialog show = new AddBulkyItemDialog(CreatePickupDate.this);
                show.show();
                show.setValuesForShow(items.get(pos));
                break;
            case R.id.contact_list:
                AddContactItemDialog show_contact = new AddContactItemDialog(CreatePickupDate.this);
                show_contact.show();
                show_contact.setValuesForShow(contacts.get(pos));
                break;
            default:
                break;
        }
    }

    @Override
    public void onSelectedDayChange(CalendarView calendarView, int year, int month, int day) {
        //Toast.makeText(getApplicationContext(), day + "." + month + "." + year, Toast.LENGTH_SHORT).show();
        Calendar selected = Calendar.getInstance();
        selected.set(year, month, day);
        if (!checkDayBefore(selected, Calendar.getInstance())) {
            Toast.makeText(getApplicationContext(), R.string.date_after_today, Toast.LENGTH_SHORT).show();
        } else {
            date = year + "-" + (month+1) + "-" + day;
        }
    }

    /**
     * Deletes a bulky item if the user pressed delete
     * @param name The name of the item
     * @param pos The position in the ListView and ArrayList
     */
    private void deleteBulkyItem(final String name, final int pos) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setCancelable(false);
        builder.setTitle(R.string.delete);
        builder.setMessage(getString(R.string.delete_bulk_1) + " " + name + " " + getString(R.string.delete_bulk_2));
        builder.setPositiveButton(getString(R.string.delete), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                // Delete item from the list
                items.remove(pos);
                bulk_adapter.remove(name);
                bulk_adapter.notifyDataSetChanged();
                dialogInterface.dismiss();
            }
        });
        builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
            }
        });
        AlertDialog delete = builder.create();
        delete.show();
    }

    /**
     * Deletes a contact item if the user pressed delete
     * @param name The name of the contact
     * @param pos The position in the ListView and ArrayList
     */
    private void deleteContact(final String name, final int pos) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setCancelable(false);
        builder.setTitle(R.string.delete);
        builder.setMessage(getString(R.string.delete_contact) + " " + name + " " + getString(R.string.delete_bulk_2));
        builder.setPositiveButton(getString(R.string.delete), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                // Delete item from the list
                contacts.remove(pos);
                contact_adapter.remove(name);
                contact_adapter.notifyDataSetChanged();
                dialogInterface.dismiss();
            }
        });
        builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
            }
        });
        AlertDialog delete = builder.create();
        delete.show();
    }

    /**
     * Collects the data from all EditTexts, ListViews, ... and returns them as a Termin object.
     * @param termin_id Is always 0
     * @return Returns a Termin object with all the data that the user entered
     */
    private Termin collectData(int termin_id) {
        Termin data = new Termin();
        data.setId(termin_id);
        data.setDate(date);
        data.setStreet(street_name.getText().toString());
        data.setPlz(postal_code.getText().toString());
        data.setBulk(items);
        data.setContacts(contacts);
        data.setCost_type(cost_type.getSelectedItem().toString());
        data.setPrice(new Integer(price.getText().toString()));
        data.setExtra_info(extra_info.getText().toString());
        data.setLongitude(longitude);
        data.setLatitude(latitude);
        return data;
    }

    /**
     * Initializes all elements for editing a pickup date and fills out
     * all elements with data from database
     */
    private void initEdit() {
        Button edit = (Button) findViewById(R.id.edit_date);
        edit.setVisibility(View.VISIBLE);
        edit.setOnClickListener(this);

        calendarView.setDate(edit_termin.getDateLong());
        street_name.setText(edit_termin.getStreet());
        postal_code.setText(edit_termin.getPlz());
        price.setText(Integer.toString(edit_termin.getPrice()));
        extra_info.setText(edit_termin.getExtra_info());
        if (edit_termin.getBulk() != null) {
            items = edit_termin.getBulk();
            for (int i = 0; i < items.size(); i++) {
                bulk_adapter.add(items.get(i).getName());
            }
            bulk_adapter.notifyDataSetChanged();
        }
        // Clear the ArrayList and the ListView because of the standard email that is inserted every time
        contacts.clear();
        contact_adapter.clear();
        contact_adapter.notifyDataSetChanged();
        if (edit_termin.getContacts() != null) {
            contacts = edit_termin.getContacts();
            for (int i = 0; i < contacts.size(); i++) {
                contact_adapter.add(contacts.get(i).getContact());
            }
            contact_adapter.notifyDataSetChanged();
        }
    }

    /**
     * Adds new bulky items and contacts to the database. It also updates already existing
     * information.
     */
    private void updateTermin() {
        // Get all bulk and contact items before changes occur
        ArrayList<Bulk> bulk_before = edit_termin.getBulk();
        ArrayList<Contact> contact_before = edit_termin.getContacts();
        // Collect all the data
        edit_termin = collectData(edit_termin.getId());
        // updates the pickup date data in the database
        taskHandler = new TaskHandler(this);
        taskHandler.setTermin(terminToList(edit_termin));
        taskHandler.execute("updateTermin");
        // Delete all old bulk items and add all new
        for (int i = 0; i < bulk_before.size(); i++) {
            taskHandler = new TaskHandler(this);
            taskHandler.setPara(new int[]{bulk_before.get(i).getId()});
            taskHandler.execute("deleteBulk");
        }
        taskHandler = new TaskHandler(this);
        taskHandler.setPara(new int[]{edit_termin.getId()});
        taskHandler.setObjList(edit_termin.getBulk());
        taskHandler.execute("createBulk");
        // Do the same with the contacts
        for (int i = 0; i < contact_before.size(); i++) {
            taskHandler = new TaskHandler(this);
            taskHandler.setPara(new int[]{contact_before.get(i).getId()});
            taskHandler.execute("deleteContact");
        }
        for (int i = 0; i < edit_termin.getContacts().size(); i++) {
            taskHandler = new TaskHandler(this);
            taskHandler.setContact(edit_termin.getContacts().get(i));
            taskHandler.setPar(edit_termin.getId());
            taskHandler.execute("createContact");
        }

    }

    private List<String> terminToList(Termin termin) {
        List<String> values = new ArrayList<String>();
        values.add(termin.getDate());
        values.add(termin.getStreet());
        values.add(termin.getPlz());
        values.add(termin.getCost_type());
        values.add(Integer.toString(termin.getPrice()));
        values.add(termin.getExtra_info());
        values.add(Double.toString(termin.getLongitude()));
        values.add(Double.toString(termin.getLatitude()));
        values.add(Integer.toString(termin.getId()));
        return values;
    }
}
